/*
 * Project: 24ClockWidget
 * Copyright (C) 2009 rdrrlabs gmail com,
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package com.rdrrlabs.a24clock.app;

import java.util.Arrays;

import android.app.Service;
import android.appwidget.AppWidgetManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;

public class ClockService extends Service {

    private static boolean DEBUG = true;
    public static String TAG = ClockService.class.getSimpleName();

    private static final Object kLock = ClockService.class;
    private static ClockThread sThread;

    private static final long DELAY_MS = 30 * 1000;

    private static class ReceiverInfo {
        public final Class<?> mReceiverClass;
        public final int mDigitsSize;
        public int[] sCachedIds = null;

        public ReceiverInfo(Class<?> clazz, int width) {
            mReceiverClass = clazz;
            mDigitsSize = width;
        }
    }

    private final static ReceiverInfo[] sReceiverInfo;

    static {
        sReceiverInfo = new ReceiverInfo[] {
            new ReceiverInfo(ClockWidgetReceiver2_2x1.class, ClockApp.DIGITS_SIZE_2x1),
            new ReceiverInfo(ClockWidgetReceiver2_3x1.class, ClockApp.DIGITS_SIZE_3x1),
            new ReceiverInfo(ClockWidgetReceiver2_4x2.class, ClockApp.DIGITS_SIZE_4x2),
            // TEMP
            new ReceiverInfo(ClockWidgetReceiver2_4x1.class, ClockApp.DIGITS_SIZE_4x1)
        };
    }

    @Override
    public IBinder onBind(Intent intent) {
        if (DEBUG) Log.d(TAG, "onBind");
        return null;
    }

    public static void start(Context context) {
        ClockThread ct;
        synchronized (kLock) {
            ct = sThread;
        }
        if (ct == null) {
            clearCachedIds();
            Intent i = new Intent(context, ClockService.class);
            context.startService(i);
        } else {
            ct.wakeUp();
        }
    }

    public static void stop() {
        synchronized (kLock) {
            if (sThread != null) {
                sThread.setCompleted();
                sThread = null;
            }
        }
    }

    public static void clearCachedIds() {
        for (ReceiverInfo rcvr : sReceiverInfo) {
            synchronized (kLock) {
                rcvr.sCachedIds = null;
            }
        }
    }

    @Override
    public void onStart(Intent intent, int startId) {
        super.onStart(intent, startId);

        if (DEBUG) Log.d(TAG, "onStart, thread=" + ((sThread==null)?"none":sThread.toString()));

        synchronized (kLock) {
            if (sThread == null) {
                sThread = new ClockThread((ClockApp) getApplicationContext());
                sThread.start();
            } else {
                sThread.wakeUp();
            }
        }
    }

    @Override
    public void onDestroy() {
        // ask thread to stop
        stop();
        super.onDestroy();
    }

    private class ClockThread extends ThreadLoop {

        private ClockApp mApp;
        private AppWidgetManager mAWMan;
        private int mNotHomeDelay = 0;
        private long mLastMinute;

        public ClockThread(ClockApp app) {
            super("ClockThread", 1 /*priorityBoost*/);
            mApp = app;
            mAWMan = AppWidgetManager.getInstance(mApp);

            mLastMinute = 0;
        }

        @Override
        protected void runIteration() {

            long now = System.currentTimeMillis();

            if (!mApp.isScreenOn()) {
                if (DEBUG) Log.d(TAG, "loop, screen on=>off");
                // stop the thread if the screen is not running
                stopThreadInternal(false /*stopService*/);
                return;
            }

            long waitNext = 0;
            boolean needUpdate = false;

            if (mApp.currentTaskIsHome()) {
                mNotHomeDelay = 0;
                mLastMinute = 0;
                needUpdate = true;
                waitNext = 1000;

            } else {
                // check again in 10 seconds if not in home
                if (mNotHomeDelay < 30) mNotHomeDelay += 5;
                if (DEBUG) Log.d(TAG, "loop, not home, wait " + Integer.toString(mNotHomeDelay));

                // however we still perform an update every minute
                long minute = now / DELAY_MS;
                long nextMinute = DELAY_MS - (now - (minute * DELAY_MS));
                if (mLastMinute == 0) {
                    mLastMinute = minute;
                } else if (mLastMinute != minute) {
                    mLastMinute = minute;
                    needUpdate = true;
                }

                waitNext = mNotHomeDelay * 1000 /*ms*/;
                if (nextMinute < waitNext) {
                    if (DEBUG) Log.d(TAG, "minute wait: " + Long.toString(nextMinute));
                    waitNext = nextMinute;
                }
            }

            // perform an update
            if (needUpdate && mAWMan != null) {
                boolean didUpdate = false;

                for (ReceiverInfo rcvr : sReceiverInfo) {
                    int[] ids;
                    synchronized (kLock) {
                        ids = rcvr.sCachedIds;
                    }
                    if (ids == null) {
                        ids = mAWMan.getAppWidgetIds(new ComponentName(mApp, rcvr.mReceiverClass));
                        if (ids != null) {
                            synchronized (kLock) {
                                rcvr.sCachedIds = ids;
                            }
                        }
                    }
                    if (ids != null) {
                        if (ids.length > 0) {
                            // Update remote view
                            mApp.updateRemoteView(mAWMan, ids, rcvr.mDigitsSize);
                            didUpdate = true;
                        }

                        if (DEBUG) Log.d(TAG, String.format("Ids[%d]: %s",
                                        ids.length,
                                        Arrays.toString(ids)));
                    }
                }


                if (!didUpdate) {
                    // No widgets to update... stop the thread AND service
                    stopThreadInternal(true /*stopService*/);
                    return;
                }
            }

            if (waitNext > 0) {
                waitNext -= System.currentTimeMillis() - now;
                if (DEBUG) Log.d(TAG, "waitNext: " + Long.toString(waitNext));
                waitFor(waitNext);
            }
        }

        private void stopThreadInternal(boolean stopService) {
            synchronized (kLock) {
                setCompleted();
                sThread = null;
                clearCachedIds();
                if (DEBUG) Log.d(TAG, "stopping update thread");
                if (stopService) {
                    ClockService.this.stopSelf();
                }
                return;
            }
        }

        @Override
        protected void endRun() {
            clearCachedIds();
            super.endRun();
        }
    }
}
